通信过程
- 配置服务器:当Spring Boot应用程序启动时,它会自动配置嵌入式的Tomcat(或Jetty、Undertow等其他服务器)
- 控制访问:使用控制器来处理HTTP请求。通常用
@RequestMapping
注解来映射HTTP请求到相应的处理方法 - 处理请求:服务器接收到请求后,解析其中的请求参数,根据URL路由到相应的控制器方法,并进行初步数据验证。控制器会将请求转发给业务逻辑层相应的服务(Service)进行处理
- 返回响应:控制器方法处理完请求后,会返回一个模型和视图(ModelAndView),或者直接返回一个对象,Spring Boot会自动将这个对象转换为JSON或XML等格式,然后服务器将这个响应发送回浏览器
控制访问
功能
- 将不同HTTP请求映射到不同处理方法
实现
@RequestMapping(properties)
- value:指定请求的实际地址
- method:指定请求的HTTP方法类型,如GET、POST、PUT、DELETE等。默认情况下,不指定method属性时
@RequestMapping
会处理所有HTTP方法 - consumes:指定处理请求的提交内容类型(Content-Type),例如application/json、application/xml等
- produces:指定返回的内容类型,仅当请求头中的Accept类型中包含该指定类型才返回
- params:指定请求中必须包含某些参数值
- headers:指定请求中必须包含某些指定的header值
@RequestMapping(value = "/path", method = RequestMethod.GET)
public responseType handlerMethod() {
// ...
}
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
处理请求
功能
- 接受前端HTTP请求,在后端进行处理,并返回需要的数据
实现
@RestController
:组合注解,相当于下面两个@Controller
:定义控制器,被自动注册为Spring的Bean,用于处理HTTP请求并生成响应,可以返回任何类型的数据,如字符串、模型对象、视图名称@ResponseBody
:
请求参数
功能
- 从HTTP请求中获取传入的参数值,以进行数据处理
实现
@RequestParam(properties)
:从HTTP请求中获取参数值,并将这些值绑定到控制器方法的参数上- value:请求的参数值
- required:该请求值是否必须
- defaultValue:如果没有该参数将使用默认值
@Controller
public class GreetingController {
@GetMapping("/greet")
@ResponseBody
public String greetUser(@RequestParam(value = "name", required = false, defaultValue = "World") String name) {
return "Hello, " + name + "!";
}
}
@RequestBody
:将HTTP请求体中的JSON或XML格式数据自动转化为对象实例
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user){
System.out.println(user);
return "OK";
}
细节
- 对于GET请求,由于没有请求体,所以不能使用
@RequestBody
@RequestBody
会将请求体中的JSON或XML格式数据转化为对象实例,这个过程需要依赖Jackson库- 在使用
@RequestBody
时,一定要确保参数类型与请求体中的数据类型相符,否则可能会出现数据解析错误的问题 - 日期参数需要用注解
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
指定日期格式
//http://localhost:8080/dateParam?updateTime=2002-11-11 00:00:00
@RequestMapping("/dateParam")
public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
System.out.println(updateTime);
return "OK";
}
- 路径参数需要用注解
@PathVariable
接收
@RequestMapping("/path/{id}/{name}")
public String pathParam(@PathVariable Integer id,@PathVariable String name) {
System.out.println(id+":"+name);
return "OK";
}
数据验证
请求格式验证
请求实体
- 使用
@Valid
或@Validated
注解结合JSR 380(Bean Validation 2.0)提供的注解(如@NotNull
、@Size
、@Pattern
等)对传入的请求实体(DTO)进行验证
// 请求实体定义
@Data
public class UserRegistrationDTO {
@NotBlank(message = "用户名不能为空") // 确保字段不为空,如果字段为空,则会抛出验证异常,并显示相应的错误消息
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 8, message = "密码长度不能少于8位") // 确保密码的长度至少为8位
private String password;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确") // 确保邮箱字段符合邮箱的格式
private String email;
@Pattern(regexp = "^[0-9]{11}$", message = "手机号码格式不正确") // 验证手机号码是否符合特定的正则表达式
private String phoneNumber;
}
// 控制器定义
@RestController
public class UserController {
@PostMapping("/register")
public ResponseEntity<String> registerUser(@Validated @RequestBody UserRegistrationDTO userRegistrationDTO) {
// 如果验证通过,继续处理注册逻辑
return ResponseEntity.ok("用户注册成功");
}
}
请求参数
- 使用
@Min
、@Max
、@DecimalMin
、@DecimalMax
等注解对请求参数进行数值范围的校验
// 控制器定义
@RestController
public class ProductController {
@GetMapping("/products")
public String getProducts(
@RequestParam @Min(0) @Max(1000) int price) {
return "查询到的商品价格范围是: " + price;
}
@GetMapping("/advancedProducts")
public String getAdvancedProducts(
@RequestParam @DecimalMin("0.00") @DecimalMax("1000.00") double price) {
return "查询到的高级商品价格范围是: " + price;
}
}
权限验证
- 使用Spring Security等安全框架进行用户身份验证和授权
- 通过配置安全规则,可以确保只有具有相应权限的用户才能访问特定的API
- 在控制器方法上使用
@PreAuthorize
、@Secured
等注解来定义方法级别的安全约束
@RestController
@RequestMapping("/books")
public class BookController {
// 允许所有用户访问
@GetMapping
public List<Book> listBooks() {
// 获取书籍列表
return bookService.getBooks();
}
// 使用 @PreAuthorize 注解
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ROLE_ADMIN')") // 只有具有ROLE_ADMIN角色的用户可以访问
public void deleteBook(@PathVariable Long id) {
// 删除书籍
bookService.deleteBook(id);
}
// 使用 @Secured 注解
@PutMapping("/{id}")
@Secured("ROLE_ADMIN") //只有具有ROLE_ADMIN角色的用户可以访问
public Book updateBook(@PathVariable Long id, @RequestBody Book book) {
// 更新书籍
return bookService.updateBook(id, book);
}
}
请求方法验证
- 使用
@RequestMapping
、@GetMapping
、@PostMapping
等注解确保请求方法(GET、POST、PUT、DELETE等)与控制器方法定义相匹配
跨域请求验证
- 使用
@CrossOrigin
注解,处理局部控制器跨域请求
@RestController
public class MyController {
// 允许来自 http://example.com 的跨域请求
@CrossOrigin(origins = "http://example.com")
@GetMapping("/api/data")
public String getData() {
return "This is some data";
}
}
- 在配置类中使用
WebMvcConfigurer
接口设置全局的跨域处理规则,通常在config包中进行配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 应用到所有的URL
.allowedOrigins("http://example.com") // 允许的域
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法
.allowedHeaders("*") // 允许的头信息
.allowCredentials(true) // 是否允许证书(cookies)
.maxAge(3600); // 预检请求的缓存时间(秒)
}
}
自定义验证
- 创建自定义注解
@Constraint
:定义这个用于验证的注解将由什么验证类来进行验证@Target
:定义注解应用对象@Retention
:定义注解保留阶段@interface
:声明这是一个注解接口
@Constraint(validatedBy = UsernameValidator.class) // 指定自定义注解将由UsernameValidator类来验证。
@Target({ ElementType.FIELD, ElementType.METHOD }) //指定自定义注解可以应用于字段(FIELD)和方法(METHOD)
@Retention(RetentionPolicy.RUNTIME) // 指定自定义注解保留到运行时
public @interface ValidUsername { // 声明了一个公共的注解接口ValidUsername
String message() default "Invalid username"; // 定义验证失败时的默认错误消息
Class<?>[] groups() default {}; // 定义这个注解属于哪些验证组
Class<? extends Payload>[] payload() default {}; // 定义这个注解携带哪些载荷,载荷可以用于进一步指定验证失败时的行为
}
- 创建验证器类
public class UsernameValidator implements ConstraintValidator<ValidUsername, String> {
@Override
public void initialize(ValidUsername constraintAnnotation) { // 设置验证器需要的任何状态或配置
// 初始化操作(如果有需要)
}
@Override
// value是需要验证的字符串,context是上下文对象,可以用来传递验证失败时的错误信息
public boolean isValid(String value, ConstraintValidatorContext context) {
// 自定义验证逻辑:这里只验证用户名是否为 "admin"
return value != null && value.equalsIgnoreCase("admin");
}
}
- 在模型类中使用自定义注解
@Data
public class User {
@NotNull
private String name;
@ValidUsername // 使用自定义的注解
private String username;
}
- 在控制器中触发验证
@RestController
public class UserController {
@PostMapping("/createUser")
public String createUser(@Valid @RequestBody User user) {
// 如果 username 不是 "admin",会抛出 ConstraintViolationException
return "User created successfully";
}
}
响应数据
功能
- 将处理好的数据封装成需要的格式并返回给前端
实现
JSON格式
- 基于JavaScript对象的表示法,语法简洁,但扩展性相对较弱,结构较为固定
- 使用
@ResponseBody
返回对象,这些对象会被自动转换成 JSON 格式
@RestController
public class MyController {
@GetMapping("/api/users")
public List<User> getUsers() {
return userService.findAll();
}
}
XML格式
- 采用自定义标签的形式,结构较为冗长,但具有良好的扩展性,可以通过自定义标签和属性灵活表示复杂数据
- 使用
@RequestMapping
注解,并设置produces
属性为 “application/xml”
@RequestMapping(value = "/api/users", produces = "application/xml")
public List<User> getUsers() {
return userService.findAll();
}
CSV格式
- 用于存储表格数据,如电子表格或数据库,但并不存储数据类型
- 每一行是一个数据记录,字段通过逗号分隔(或其他字符,如制表符、分号等)
- CSV文件是纯文本文件,格式简单,便于跨平台
- 使用
@RequestMapping
注解,并设置produces
属性为 “text/csv”
@ResponseBody
@RequestMapping(value = "/api/users/csv", produces = "text/csv")
public ResponseEntity<String> downloadCsv() {
String csvData = convertListToCsv(userService.findAll());
return ResponseEntity // 创建了一个 ResponseEntity 对象
// 表示HTTP状态码200(OK),告诉前端请求成功
.ok()
// 设置了HTTP响应头Content-Disposition,告诉浏览器这是一个附件,并且建议的文件名为users.csv
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.csv")
// 设置了HTTP响应的正文,即之前转换得到的CSV格式的字符串数据。
.body(csvData);
}
自定义格式
功能
- 使用
ResponseEntity
类,在控制器方法中封装 HTTP 响应的状态码、头信息和响应体,灵活地控制 HTTP 响应的内容
实现
- 封装响应数据:JSON、XML、CSV、String …
- 设置HTTP状态码:200 OK、404 Not Found、500 Internal Server Error …
- 设置响应头:内容类型(Content-Type)、缓存控制(Cache-Control)、跨域请求(CORS)…
// 通过构造器返回
public ResponseEntity(T body, HttpStatus status, HttpHeaders headers) //构造函数
@GetMapping("/headers")
public ResponseEntity<String> getWithHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "HeaderValue");
return new ResponseEntity<>("Response with custom header", headers, HttpStatus.OK);
}
// 通过链式调用返回 (每个方法都会返回 ResponseEntity.BodyBuilder 本身)
@GetMapping("/download")
public ResponseEntity<String> downloadCsv() {
String csvData = "name,age,email\nAlice,30,alice@example.com\nBob,25,bob@example.com";
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.csv")
.body(csvData);
}
细节
ResponseEntity
类位于org.springframework.http
包中
统一响应封装
功能
- 确保前端和后端之间的交互加便捷和统一,便于前端预期后端返回的数据结构,从而简化前端逻辑处理,增强系统的稳定性和可维护性
实现
- 封装Result实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
/**
* 响应状态码
* 200:成功
* 400:客户端请求错误
* 500:服务器内部错误
*/
private int code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
/**
* 分页信息
*/
private Pagination pagination;
/**
* 构造函数,用于成功的响应
* @param data 响应数据
*/
public ApiResponse(T data) {
this.code = 200;
this.message = "Success";
this.data = data;
}
/**
* 构造函数,用于错误的响应
* @param message 错误消息
*/
public ApiResponse(String message) {
this.code = 500;
this.message = message;
}
/**
* 分页信息类
*/
@Data
public static class Pagination {
private int page; // 当前页码
private int size; // 每页大小
private long total; // 总记录数
private int totalPages;// 总页数
public Pagination(int page, int size, long total) {
this.page = page;
this.size = size;
this.total = total;
this.totalPages = (int) Math.ceil((double) total / size);
}
}
/**
* 创建成功的响应
* @param data 响应数据
* @return ApiResponse实例
*/
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(data);
}
/**
* 创建成功的响应,包含分页信息
* @param data 响应数据
* @param page 当前页码
* @param size 每页大小
* @param total 总记录数
* @return ApiResponse实例
*/
public static <T> ApiResponse<T> success(T data, int page, int size, long total) {
ApiResponse<T> response = new ApiResponse<>(data);
response.setPagination(new Pagination(page, size, total));
return response;
}
/**
* 创建错误的响应
* @param message 错误消息
* @return ApiResponse实例
*/
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(message);
}
}
Last updated on